﻿/*
* MIT License
*
* Copyright (c) 2017 wen.gu <454727014@qq.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

/***************************************************************************
* Name: tiny_ring_buffer.c
*
* Purpose: ring buffer implementation
*
* Developer:
*   wen.gu , 2018-08-07
*
* TODO:
*
***************************************************************************/

#include <string.h>

#include "general_error.h"
#include "tiny_ring_buffer.h"


/***************************************************************************
*
* macro define
*
***************************************************************************/
#define LOGE printf
#define LOGD printf
#define LOGI printf
/***************************************************************************
*
* data structure define
*
***************************************************************************/
typedef struct my_ring_buffer_s
{
    tiny_ring_buffer_t common;

    GBOL is_owns_data; /** GTRUE: ring buffer memory allocated by itself */
    GU32 free_size; /** recording free memory size in ring buffer */
    GU32 cached_size; /** recording cached data size in ring buffer */

    GU08 *end_ptr; /** the end pointer of ring buffer */
    GU08 *write_ptr;
    GU08 *read_ptr;
    GU08 *trail_ptr; /** the end pointer of cached data */
}my_ring_buffer_t;

/***************************************************************************
*
* inner API define
*
***************************************************************************/

static GS32 tfRBCheckRequestSize(my_ring_buffer_t* mrb, 
                                GU32 size, GBOL* is_need_turnback_ptr)
{
    GS32 ret = G_ErrorNoMemory;
    *is_need_turnback_ptr = GFALSE;

    if (size <= mrb->free_size)
    {
        if (mrb->read_ptr <= mrb->write_ptr)
        {
            GU32 trail_size = (mrb->end_ptr - mrb->write_ptr);

            if (trail_size >= size)
            {
                ret = G_OK;
            }
            else
            {
                /** not enough memory for request in tailer of buffer,
                * so need turnback and check memory size from the header of buffer
                */
                if ((mrb->free_size - trail_size) >= size)
                {
                    *is_need_turnback_ptr = GTRUE;
                    ret = G_OK;
                }
            }

        }
        else
        {
            /** in this case free size == mrb->read_ptr - mrb->write_ptr ,
            *  so needn't compute tail_size,
            */
            ret = G_OK;
        }
    }

    return ret;
}

/** 翻转写指针 到ring buffer头部 */
static inline void tfRBTurnbackWritePtr(my_ring_buffer_t* mrb)
{
    mrb->free_size -= (mrb->end_ptr - mrb->write_ptr);
    mrb->trail_ptr = mrb->write_ptr;
    mrb->write_ptr = mrb->common.data;
}

/** 更新写指针 */
static inline void tfRBUpdateWritePtr(my_ring_buffer_t* mrb, GS32 size)
{
    mrb->write_ptr += size;
    mrb->free_size -= size;
    mrb->cached_size += size;
}

/** 翻转读指针 到ring buffer 头部 */
static inline void tfRBTurnbackReadPtr(my_ring_buffer_t* mrb)
{
    mrb->free_size += (mrb->end_ptr - mrb->read_ptr);
    mrb->read_ptr = mrb->common.data;
    mrb->trail_ptr = NULL;
}

/** 更新读指针 */
static inline void tfRBUpdateReadPtr(my_ring_buffer_t* mrb, GS32 size)
{
    mrb->read_ptr += size;
    mrb->free_size += size;
    mrb->cached_size -= size;
}

/** 获取从读指针开始的连续有效数据长度 */
static inline GU32 tfRBGetValidDataSize(my_ring_buffer_t* mrb)
{
    return (mrb->trail_ptr) ? (mrb->trail_ptr - mrb->read_ptr) : (mrb->write_ptr - mrb->read_ptr);
}

///////////////////////////////////////////////////////////////////////////


/***************************************************************************
*
* API define
*
***************************************************************************/

GS32 tfRingBufferCreate(tiny_ring_buffer_t** trb, GU32 size, void* data)
{
    GS32 ret = G_ErrorBadParameter;

    if (trb && (size > 0))
    {
        my_ring_buffer_t* mrb = (my_ring_buffer_t*)malloc(sizeof(my_ring_buffer_t));

        if (mrb)
        {
            GU08* base_ptr = data;
            memset(mrb, 0, sizeof(*mrb));

            if (data == NULL)
            {
                base_ptr = (GU08*)malloc(size);
                mrb->is_owns_data = GTRUE;
            }

            if (base_ptr)
            {
                tiny_ring_buffer_t* temp = (tiny_ring_buffer_t*)mrb;

                temp->data = base_ptr;
                temp->size = size;
                temp->last_error = G_OK;

                tfRingBufferReset(temp);

                *trb = temp;

                ret = G_OK;
            }
            else
            {
                free(mrb);
            }
        }
        else
        {
            LOGE("allocate ring buffer intance failed\n");
            ret = G_ErrorInsufficientResources;
        }
    }

    return ret;
}

void tfRingBufferDestroy(struct tiny_ring_buffer_s* trb)
{
    my_ring_buffer_t* mrb = (my_ring_buffer_t*)trb;

    if ((mrb->is_owns_data == GTRUE) && (trb->data))
    {
        free(trb->data);
    }

    free(mrb);
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

void tfRingBufferReset(struct tiny_ring_buffer_s* trb)
{
    my_ring_buffer_t* mrb = (my_ring_buffer_t*)trb;

    mrb->read_ptr = trb->data;
    mrb->write_ptr = trb->data;
    mrb->end_ptr = trb->data + trb->size;
    mrb->trail_ptr = NULL;

    mrb->free_size = trb->size;
    mrb->cached_size = 0;
}

GU32 tfRingBufferGetCachedSize(struct tiny_ring_buffer_s* trb)
{
    return ((my_ring_buffer_t*)trb)->cached_size;
}

GU32 tfRingBufferGetFreeSpace(struct tiny_ring_buffer_s* trb)
{
    return ((my_ring_buffer_t*)trb)->free_size;
}

void* tfRingBufferAlloc(struct tiny_ring_buffer_s* trb, GU32 size)
{
    my_ring_buffer_t* mrb = (my_ring_buffer_t*)trb;
    GU08 *req_buf = NULL;
    GS32 ret = G_ErrorBadParameter;

    if (size > 0)
    {
        if (mrb->free_size > 0)/** have free size */
        {
            GBOL is_need_turnback = GFALSE;

            /** 检测请求size */
            ret = tfRBCheckRequestSize(mrb, size, &is_need_turnback);

            if (G_OK == ret)
            {
                if (is_need_turnback)
                {/** 如果需要翻转写指针，就调用相关函数 */
                    tfRBTurnbackWritePtr(mrb);
                }

                /** check condition ok, so allocate memory */
                req_buf = mrb->write_ptr;
                /** 获取到需要的memory之后，更新写指针 */
                /** update write ptr */
                tfRBUpdateWritePtr(mrb, size);
            }
        }
        else
        {
            ret = G_ErrorNoMemory;
        }
    }

    trb->last_error = ret; /** recording error code */

    return req_buf;
}

void* tfRingBufferRealloc(struct tiny_ring_buffer_s* trb, GU32 new_size, void* old_ptr, GU32 old_size)
{
    my_ring_buffer_t* mrb = (my_ring_buffer_t*)trb;
    GS32 ret = G_ErrorBadParameter;
    void* new_ptr = NULL;

    if (new_size > 0)
    {
        if (old_ptr && (old_size > 0))
        {
            if (((GU08*)old_ptr + old_size) == mrb->write_ptr)
            {
                if (new_size <= old_size)
                {
                    GS32 revert_Size = old_size - new_size;

                    tfRBUpdateWritePtr(mrb, -revert_Size);
                    new_ptr = old_ptr;
                    ret = G_OK;
                }
                else
                {
                    GS32 revert_Size = old_size;
                    tfRBUpdateWritePtr(mrb, -revert_Size); /**rollback write ptr*/

                    GBOL is_need_turnback_write_ptr = GFALSE;
                    ret = tfRBCheckRequestSize(mrb, new_size, &is_need_turnback_write_ptr);

                    if (G_OK == ret)
                    {
                        if (is_need_turnback_write_ptr == GTRUE)
                        {
                            //turnback wirte pointer
                            tfRBTurnbackWritePtr(mrb);
                            //copy old data
                            memcpy(mrb->write_ptr, old_ptr, old_size);
                        }

                        new_ptr = mrb->write_ptr;

                        /** update write ptr */
                        tfRBUpdateWritePtr(mrb, new_size);
                    }
                    else
                    {/** if alloc failed, then recovery write ptr */
                        tfRBUpdateWritePtr(mrb, revert_Size);
                    }
                }
            }
            else
            {
                /**should not be here */
            }
        }
        else
        {
            new_ptr = tfRingBufferAlloc(trb, new_size);
            ret = trb->last_error;
        }
    }

    trb->last_error = ret;

    return new_ptr;
}

void  tfRingBufferFree(struct tiny_ring_buffer_s* trb, void* ptr, GU32 size)
{
    my_ring_buffer_t* mrb = (my_ring_buffer_t*)trb;
    GS32 ret = G_ErrorBadParameter;
    GU08* buf = (GU08*)ptr;

    if (buf && (size > 0))
    {
        if ((trb->data <= buf) && (buf < mrb->end_ptr))
        {
            if (mrb->read_ptr == mrb->trail_ptr)
            {
                tfRBTurnbackReadPtr(mrb);
            }

            if (ptr == mrb->read_ptr)
            {
                GU32 valid_data_size = tfRBGetValidDataSize(mrb); /** valid trail data size */

                if (size <= valid_data_size)
                {
                    tfRBUpdateReadPtr(mrb, size);
                    ret = G_OK;
                }
                else
                {
                    LOGE("request free size(%d) > valid data size(%d)\n", size, valid_data_size);
                }
            }
            else
            {
                LOGE("free memory need follow allcate order(like FIFO)\n");
            }
        }
        else
        {
            LOGE("request free buffer(%p, size:%d) out of range(%p - %p)\n",
                ptr, size, trb->data, mrb->end_ptr);
        }
    }

    trb->last_error = ret;
}

void tfRingBufferDump(struct tiny_ring_buffer_s* trb)
{
    my_ring_buffer_t* mrb = (my_ring_buffer_t*)trb;
    LOGI("mHead(%p), mEnd(%p), write_ptr(%p) ,read_ptr(%p) \n",
        trb->data, mrb->end_ptr, mrb->write_ptr, mrb->read_ptr);
}

